package lang

import (
	"fmt"
	"os"
	"runtime"
)

//a parse tree
type Tree struct {
	Name      string     // name of what we're parsing, for error reports
	Root      *GroupNode //or *Node? XXX
	lex       *lexer
	token     [2]item
	peekCount int
	vars      []string
	errexp    bool //are we expecting a panic?
}

func (t *Tree) next() (tok item) {
	if t.peekCount > 0 {
		t.peekCount--
		//pc, _, line, _ := runtime.Caller(1)
		//fmt.Printf("peekCount decreased by %s on line %d, to %d\n", runtime.FuncForPC(pc).Name(), line, t.peekCount)
	} else {
		t.token[0] = t.lex.nextItem()
		if t.token[0].typ == itemError {
			t.errorf("%s", tok)
		}
	}
	//fmt.Print(tok.String() + " ")
	return t.token[t.peekCount]
}

func (t *Tree) backup() {
	t.peekCount++
	//pc, _, line, _ := runtime.Caller(1)
	//fmt.Printf("peekCount increased by %s on line %d, to %d\n", runtime.FuncForPC(pc).Name(), line, t.peekCount)
	if t.peekCount > 1 {
		panic("bad peek")
	}
}

func (t *Tree) backup2(t1 item) {
	t.token[1] = t1
	t.peekCount = 2
}

func (t *Tree) peek() item {
	if t.peekCount > 0 {
		return t.token[t.peekCount-1]
	}
	t.peekCount = 1
	t.token[0] = t.lex.nextItem()
	return t.token[0]
}

func NewTree(name string) *Tree {
	return &Tree{Name: name}
}

func (t *Tree) errorf(format string, args ...interface{}) {
	t.Root = nil
	t.errexp = true
	format = fmt.Sprintf("%s:%d %s", t.Name, t.lex.lineNumber(), format)
	panic(fmt.Errorf(format, args...))
}

func (t *Tree) error(err os.Error) {
	t.errorf("%s", err)
}

func (t *Tree) expect(expected itemType, context string) item {
	token := t.next()
	if token.typ != expected {
		t.errorf("expected %s in %s; got %s", expected, context, token)
	}
	return token
}

func (t *Tree) unexpected(token item, context string) {
	t.errorf("unexpected %s in %s (next 2: %s %s   )", token, context, t.next(), t.next())
}

func (t *Tree) recover(errp *os.Error) {
	if !t.errexp {
		return
	}
	e := recover()
	if e != nil {
		if _, ok := e.(runtime.Error); ok {
			panic(e)
		}
		if t != nil {
			t.stopParse()
		}
		*errp = e.(os.Error)
	}
	return
}

func (t *Tree) startParse(lex *lexer) {
	t.Root = nil
	t.lex = lex
	t.vars = []string{"nil", "print"}
}

func (t *Tree) stopParse() {
	t.lex = nil
	t.vars = nil
}

func (t *Tree) Parse(s string) (tree *Tree, err os.Error) {
	defer t.recover(&err)
	t.startParse(lex(t.Name, s))
	t.topParse()
	t.stopParse()
	return t, nil
}
//root scope
func (t *Tree) topParse() {
	t.Root = newGroup()
	//XXX FUCK IT ALL CODE MUST BE IN FUNCTIONS
	for k := t.next(); k.typ != itemEOF; k = t.next() {
		switch k.typ {
		/*case itemIdent, itemLParen:
			if k.typ == itemIdent {
				if nt := t.peek(); nt.typ == itemEquals {
					t.Root.Append(newVarDef(k.val, t.parseExpr(t.next())))
					continue
				}
			}
			n := t.parseExpr(k)
			t.Root.Append(n)
		case itemIf:
			t.Root.Append(t.parseBodyExpr(k))
		case itemWhile:
			t.Root.Append(t.parseWhile())*/
		case itemFunc:
			t.parseFunc()
		case itemReturn:
			//t.Root.Append(t.parseReturn())
		default:
			t.unexpected(k, "root")
		}
	}
	/*	k := t.next()
		for k.typ != itemError {
			switch k.typ {
			case itemIdent:
				z := t.next()
				switch z.typ {
				case itemLParen:
					t.backup()
					t.Root.Append(newCall(k.val, t.parseArgs()))
				case itemEquals:
					t.Root.Append(newVarDef(k.val, t.parseExpr(t.next())))
				default:
					t.unexpected(z, "function call or variable assignment")
				}
			case itemFunc: //a function definition
				t.parseFunc()
			case itemReturn:
				t.Root.Append(t.parseReturn())
			case itemEOF:
				return
			default:
				t.unexpected(k, "root")
			}
			k = t.next()
		}*/
}

func (t *Tree) parseExpr(i item) Node {
	lhs := t.parseSinExpr(i)
	switch lhs.Type() {
	case NodeBranch:
		t.errorf("Unexpected branch in non-body")
	}
	lhs = t.parseBinOp(0, lhs)
	/*op := t.peek()
	switch op.typ {
	case itemPlus, itemMinus, itemMult, itemDiv:
		//TODO precedence
		t.next()
		oper := getBinOp(op)
		rhs := t.parseSinExpr(t.next())
		//merge lhs and rhs
		fmt.Printf("Merging binop: %s --- %s --- %s\n", lhs, oper, rhs)
		lhs = newBinOp(oper, lhs, rhs)
	}*/
	return lhs
}
//FIXME refactor me
func (t *Tree) parseBodyExpr(i item) Node {
	lhs := t.parseSinExpr(i)
	switch lhs.Type() {
	case NodeBranch:
		return lhs //don't do arithmetic check
	}
	lhs = t.parseBinOp(0, lhs)
	/*op := t.peek()
	switch op.typ {
	case itemPlus, itemMinus, itemMult, itemDiv:
		t.next()
		oper := getBinOp(op)
		rhs := t.parseSinExpr(t.next())
		fmt.Printf("Merging binop: %s --- %s --- %s\n", lhs, oper, rhs)
		lhs = newBinOp(oper, lhs, rhs)
	}*/
	return lhs
}
func (t *Tree) parseBinOp(prec int, lhs Node) Node {
	var (
		op BinOp
		rhs Node
	)
	for {
		//simple: if next item is not a binop then we just return lhs
		kop, opok := binOps[t.peek().typ]
		if !opok {
			//not in mapping, so not a binop
			return lhs
		}
		op = kop //fucking wtf
		//otherwise consume it
		//fmt.Printf("(%d)Starting binop on %s\n", prec, t.peek())
		t.next()
		//not sure I need this check XXX
		if preced[op] < prec {
			return lhs
		}
		rhs = t.parseSinExpr(t.next())
		nop, _ := binOps[t.peek().typ]
		/*if !nopok {
			fmt.Printf("(%d)Aborting continuation of binop on %s\n", prec, t.peek())
			//return newBinOp(op, lhs, rhs)
			break
		}???*/
		//consume
		if preced[op] < preced[nop] {
		//	fmt.Printf("Shift!\n")
			//t.next()
			rhs = t.parseBinOp(prec+1, rhs)
		} //else {
		//	fmt.Printf("NOT SHIFTING: %d !< %d (%d)\n", preced[op], preced[nop], prec)
		//}
		//you forgot to merge you dumbass
		//fmt.Printf("Merging %s with %s ::: %s\n", lhs, rhs, op)
		lhs = newBinOp(op, lhs, rhs)
		//return newBinOp(op, lhs, rhs)
		//XXX XXX XXX maybe we need an next() here
	}
	fmt.Printf("FINAL!!!!! Merging %s with %s ::: %s\n", lhs, rhs, op)
	return newBinOp(op, lhs, rhs)
}
/*func (t *Tree) parseBinOp(prec int, lhs Node) Node {
	var (
		op BinOp
		rhs Node
	)
	for {
		nt := t.peek()
		if ! (nt.typ > itemBINOP && nt.typ < itemBinEnd) {
			//it's not more operator, so bye
			//fmt.Printf("BinOpDone %d | (%s - %s)\n", prec, nt, binOp[op])
			return lhs
		}
		//fmt.Println(nt)
		//fmt.Println("BINOP :O")
		//otherwise get it
		nt = t.next()
		op = binOps[nt.typ]
		//fmt.Printf("Binop seek: %s\n", binOp[op])
		//if the op binds at least as tightly as current, consume it, otherwise we're done
		if preced[op] < prec {
			fmt.Printf("Same level\n")
			return lhs
		} //wat
		rhs = t.parseExpr(t.next())
		//fmt.Printf("BinOp parsed rhs: %s\n", rhs)
		//whats next (why...) TODO REFACTOR THIS STUFF ITS PROBABLY CRAP
		nop := t.peek()
		if ! (nop.typ > itemBINOP && nop.typ < itemBinEnd) {
			break
		}
		//fmt.Printf("Not Aborting on %s (%d - > %d, < %d\n", nop, int(nop.typ), int(itemBINOP), int(itemBinEnd))
		noper := binOps[nop.typ]
		if preced[op] < preced[noper] {
			rhs = t.parseBinOp(prec+1, rhs)
		}
		//it's a binop, nowai
		nop = t.next()

	}
	zlhs := newBinOp(op, lhs, rhs)
	//fmt.Printf("I made a binop:   %s\n", zlhs)
	return zlhs
}*/

func (t *Tree) parseSinExpr(i item) Node {
	switch i.typ {
	case itemString:
		return newStr(i.val)
	case itemRawString:
		return newRStr(i.val)
	case itemNumber:
		num, err := newNumber(i.val)
		if err != nil {
			t.errorf("%s", err)
		}
		return num
	case itemIdent:
		nt := t.peek()
		if nt.typ == itemLParen {
			return newCall(i.val, t.parseArgs())
		}
		return newIdent(i.val)
	case itemBool:
		return newBool(i.val == "true")
	case itemLParen:
		t.backup()
		//panic("debug")
		exp := t.parseArgs() //TODO generalize parseArgs
		return exp.Args
		//parse a ( ) expression
		/*gr := newGroup()
		tk := t.next()
		for tk.typ != itemRParen {
			gr.Append(t.parseExpr(tk))
			tk = t.next()
			if tk.typ != itemRParen {
				t.expect(itemComma, "parenthesized expression")
			}
		}*/
	case itemIf:
		cond := t.parseCond()
		body := t.parseBody()
		body2 := t.parseElse()
		return newBranch(body, body2, cond)
	case itemWhile:
		return t.parseWhile()

	default:
		t.unexpected(i, "expression")
	}
	panic("mustn't get here")
}

func (t *Tree) parseWhile() *LoopNode {
	cond := t.parseCond()
	body := t.parseBody()
	return newLoop(nil, cond, nil, body)
}

func (t *Tree) parseElse() *GroupNode {
	//do we have an else?
	if t.peek().typ != itemElse {
		return nil
	}
	//gobble it up
	t.next()
	//now parse another body expr, lol
	return t.parseBody()
}

func (t *Tree) parseFunc() {
	//we have the <func>, we need a name for this function
	name := t.expect(itemIdent, "function declaration")
	//now args
	args := newGroup()
	t.expect(itemLParen, "function declaration")
	for itm := t.next(); itm.typ == itemIdent; itm = t.next() {
		//we know itm must be an identifier
		args.Append(newIdent(itm.val))
		n := t.peek()
		if n.typ == itemComma {
			t.next() //skip over the comma
			continue
		} else if n.typ == itemRParen {
			//we're done, but don't consume the paren here
			// or maybe we do...?
			t.next()
			break
		} else {
			t.unexpected(n, "function declaration")
		}
	}
	bdg := t.parseBody()
	//paren is consumed, so the next thing needs to be the body
	//and our body group:
	//okay, we have all the pieces we need
	t.Root.Append(newDefun(name.val, args, bdg))

}

//parse the ( ) part of an if, etc. first token must be the (
func (t *Tree) parseCond() Node {
	t.expect(itemLParen, "condition")
	stmt := t.parseExpr(t.next())
	t.expect(itemRParen, "condition")
	return stmt
}

func (t *Tree) parseBody() *GroupNode {
	bdg := newGroup()
	t.expect(itemLBrace, "block body")
	fbe := t.next()
Loop:
	for {
		switch fbe.typ {
		case itemReturn:
			bdg.Append(t.parseReturn())
		case itemRBrace:
			break Loop
		default:
			bdg.Append(t.parseBodyExpr(fbe))
		}
		fbe = t.next()
	}
	return bdg
}

func (t *Tree) parseReturn() Node {
	nt := t.peek()
	switch {
	case nt.typ == itemEOF, nt.typ == itemRBrace, nt.typ > itemKeyword:
		return newReturn(nil)
	default:
		return newReturn(t.parseExpr(t.next()))
	}
	panic("can't get here")
}

func (t *Tree) parseArgs() *ArgListNode {
	t.expect(itemLParen, "argument list")
	id := t.next()
	/*if id.typ != itemRParen && id.typ != itemIdent {
		t.unexpected(k, "argument list")
	}*/
	//args := make([]Node, 0, 1)
	args := newGroup()
	for id.typ != itemRParen {
		exp := t.parseExpr(id)
		args.Append(exp)
		n := t.peek()
		if n.typ == itemRParen {
			t.next()
			break
		}
		t.expect(itemComma, "argument list")
		id = t.next()
	}
	return newArgList(args)
	/*for id.typ != itemRParen {
		switch id.typ {
		case itemIdent, itemBool, itemNumber, itemRawString, itemString, itemLParen:
			//args = append(args, t.parseExpr(id))
			args.Append(t.parseExpr(id))
		case itemComma:
			break
		default:
			t.unexpected(id, "argument list")
		}
		id = t.next()
	}
	return newArgList(args)*/
}
